/******************************************************************************/
/*  Class AT25DFxxx                                                           */
/*  PO: S. Maslyakov, rusoil.9@gmail.com                                      */
/*                                                                            */
/*  Revision:     1.0                                                         */
/*  Date:         2010/12/21 18:46:33                                         */
/******************************************************************************/


#include "data_types.h"
#include "Core\core.h"
#include "Spi\spi.h"
#include "At25df\at25df.h"



//==============================================================================
// App: Init
//==============================================================================
void AT25DF::Init_At25df() {
    uint8_t id[3];

    // Conf cs
    PINSEL1_bit.P0_16 = 0;                // CS
    FIO0DIR_bit.P0_16 = 1;                // CS
    PINMODE1_bit.P0_16 = 0;               // 0=PULL_UP

    LockChip();

    // Read Manufacturer and Device ID information 
    ReadId(id);

    if (id[0] == ATMEL_MANUF_ID) {
        m_familyCode = (id[1] >> 5);
        m_densityCode = (id[1] & 0x1F);
        m_isFlashOk = TRUE_T;
    };
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Read status register
//==============================================================================
uint16_t AT25DF::ReadStatusReg() {
    uint16_t sreg;

    // Test flash memory is Ok
    if (m_isFlashOk != TRUE_T) {
        return 0x00FC;
    };

    SelectChip();

    SPI_BUFF = eAT25DF_READ_SREG;
    while (!(SPI_FREE));

    // Read status reg (16-bit)
    SPI_BUFF = AT25DF_SPI_READ_CLK;
    while (!(SPI_FREE));
    sreg = SPI_BUFF;
    sreg <<= 8;

    SPI_BUFF = AT25DF_SPI_READ_CLK;
    while (!(SPI_FREE));
    sreg |= SPI_BUFF;

    LockChip();

    return ((sreg >> 8) | (sreg << 8));
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Read id
//==============================================================================
void AT25DF::ReadId(uint8_t * const _pId) {
    SelectChip();

    SPI_BUFF = eAT25DF_READ_ID;
    while (!(SPI_FREE));

    // Read ID
    SPI_BUFF = AT25DF_SPI_READ_CLK;
    while (!(SPI_FREE));
    _pId[0] = SPI_BUFF;

    SPI_BUFF = AT25DF_SPI_READ_CLK;
    while (!(SPI_FREE));
    _pId[1] = SPI_BUFF;

    SPI_BUFF = AT25DF_SPI_READ_CLK;
    while (!(SPI_FREE));
    _pId[2] = SPI_BUFF;

    LockChip();
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Write enable
//==============================================================================
void AT25DF::WriteEnable() {
    SelectChip();

    SPI_BUFF = eAT25DF_WRITE_EN;
    while (!(SPI_FREE));

    LockChip();
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Write disable
//==============================================================================
void AT25DF::WriteDisable() {
    SelectChip();

    SPI_BUFF = eAT25DF_WRITE_DIS;
    while (!(SPI_FREE));

    LockChip();
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Test at busy
//==============================================================================
bool_t AT25DF::IsBusy() {
    const uint16_t statusReg = ReadStatusReg();

    return (statusReg & AT25DF_BUSY) ? TRUE_T : FALSE_T;
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Test at write enable
//==============================================================================
bool_t AT25DF::IsWriteEnable() {
    const uint16_t statusReg = ReadStatusReg();

    return (statusReg & AT25DF_WEL) ? TRUE_T : FALSE_T;
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Protect sector
//==============================================================================
uint32_t AT25DF::ProtectSector(const __AT25DF_ADDRESS _addr) {

    // Test write enable bit
    if (IsWriteEnable() == FALSE_T) {
        WriteEnable();

        if (IsWriteEnable() == FALSE_T) {
            return eMEM_WRITE_EN_ERROR;
        };
    };

    SelectChip();

    // Write cmd
    SPI_BUFF = eAT25DF_PROTECT_SECTOR;

    // Write address
    for (uint32_t i = 0, k = 2; i < 3;) {
        if (SPI_FREE) {
            SPI_BUFF = _addr.addrArray[k--];
            i++;
        };
    };
    while (!(SPI_FREE));

    LockChip();

    return eMEM_OK;
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Unprotect sector
//==============================================================================
uint32_t AT25DF::UnprotectSector(const __AT25DF_ADDRESS _addr) {

    // Test write enable bit
    if (IsWriteEnable() == FALSE_T) {
        WriteEnable();

        if (IsWriteEnable() == FALSE_T) {
            return eMEM_WRITE_EN_ERROR;
        };
    };

    SelectChip();

    // Write cmd
    SPI_BUFF = eAT25DF_UNPROTECT_SECTOR;

    // Write address
    for (uint32_t i = 0, k = 2; i < 3;) {
        if (SPI_FREE) {
            SPI_BUFF = _addr.addrArray[k--];
            i++;
        };
    };
    while (!(SPI_FREE));

    LockChip();

    return eMEM_OK;
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Read sector protection register
//==============================================================================
bool_t AT25DF::IsSectorProtect(const __AT25DF_ADDRESS _addr) {

    SelectChip();

    // Write cmd
    SPI_BUFF = eAT25DF_READ_PROT_REG;

    // Write address
    for (uint32_t i = 0, k = 2; i < 3;) {
        if (SPI_FREE) {
            SPI_BUFF = _addr.addrArray[k--];
            i++;
        };
    };
    while (!(SPI_FREE));

    // Read reg
    SPI_BUFF = AT25DF_SPI_READ_CLK;
    while (!(SPI_FREE));
    const uint8_t reg = SPI_BUFF;

    LockChip();

    return (reg == 0) ? FALSE_T : TRUE_T;
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Read data bytes
//==============================================================================
uint32_t AT25DF::ReadDataBytes(const __AT25DF_DATA * const _pData) {
    // Test flash memory is Ok
    if (m_isFlashOk != TRUE_T) {
        return eMEM_NOT_AVAILABLE;
    };

    // Validate addr
    if (_pData->addr.addr32 >= AT25DF_FLASH_SIZE) {
        return eMEM_ADDR_ERROR;
    };

    // Test busy
    if (IsBusy() == TRUE_T) {
        return eMEM_BUSY;
    };

    SelectChip();

    // Write cmd
    SPI_BUFF = eAT25DF_READ_DATA;

    // Write address
    for (uint32_t i = 0, k = 2; i < 3;) {
        if (SPI_FREE) {
            SPI_BUFF = _pData->addr.addrArray[k--];
            i++;
        };
    };
    while (!(SPI_FREE));

    // Read data
    uint32_t ind = 0;
    const uint32_t len = _pData->dataLen - 1;

    SPI_BUFF = AT25DF_SPI_READ_CLK;
    while (ind < len) {
        if (SPI_FREE) {
            _pData->pBuff[ind++] = SPI_BUFF;
            SPI_BUFF = AT25DF_SPI_READ_CLK;
        };
    };
    while (!(SPI_FREE));
    _pData->pBuff[ind] = SPI_BUFF;

    LockChip();

    return eMEM_OK;
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Program page
//==============================================================================
uint32_t AT25DF::ProgramPage(const __AT25DF_DATA * const _pData) {
    // Test flash memory is Ok
    if (m_isFlashOk != TRUE_T) {
        return eMEM_NOT_AVAILABLE;
    };

    // Validate addr
    if (_pData->addr.addr32 >= AT25DF_FLASH_SIZE) {
        return eMEM_ADDR_ERROR;
    };

    // Validate addr + dataLen
    const uint32_t freeSize = AT25DF_PAGE_SIZE - _pData->addr.addrArray[0];
    if (_pData->dataLen > freeSize) {
        return eMEM_DATA_ERROR;
    };

    // Test busy
    if (IsBusy() == TRUE_T) {
        return eMEM_BUSY;
    };

    // Test sector protect
    if (IsSectorProtect(_pData->addr) == TRUE_T) {
        UnprotectSector(_pData->addr);

        if (IsSectorProtect(_pData->addr) == TRUE_T) {
            return eMEM_WRITE_EN_ERROR;
        };
    };

    // Test write enable bit
    if (IsWriteEnable() == FALSE_T) {
        WriteEnable();

        if (IsWriteEnable() == FALSE_T) {
            return eMEM_WRITE_EN_ERROR;
        };
    };

    SelectChip();

    // Write cmd
    SPI_BUFF = eAT25DF_PRGR_PAGE;

    // Write address
    for (uint32_t i = 0, k = 2; i < 3;) {
        if (SPI_FREE) {
            SPI_BUFF = _pData->addr.addrArray[k--];
            i++;
        };
    };

    // Write data
    for (uint32_t i = 0; i < (_pData->dataLen); ) {
        if (SPI_FREE) {
            SPI_BUFF = _pData->pBuff[i++];
        };
    };
    while (!(SPI_FREE));

    LockChip();

    return eMEM_OK;
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Block erase
//==============================================================================
uint32_t AT25DF::BlockErase(const __AT25DF_ADDRESS _addr, const __eAt25DF_BLOCK _block) {
    // Test flash memory is Ok
    if (m_isFlashOk != TRUE_T) {
        return eMEM_NOT_AVAILABLE;
    };

    // Validate addr
    if (_addr.addr32 >= AT25DF_FLASH_SIZE) {
        return eMEM_ADDR_ERROR;
    };

    // Test busy
    if (IsBusy() == TRUE_T) {
        return eMEM_BUSY;
    };

    // Test sector protect
    if (IsSectorProtect(_addr) == TRUE_T) {
        UnprotectSector(_addr);

        if (IsSectorProtect(_addr) == TRUE_T) {
            return eMEM_WRITE_EN_ERROR;
        };
    };

    // Test write enable bit
    if (IsWriteEnable() == FALSE_T) {
        WriteEnable();

        if (IsWriteEnable() == FALSE_T) {
            return eMEM_WRITE_EN_ERROR;
        };
    };

    // Select block
    __eAT25DF_INSTRUCTIONS instr;
    switch (_block)
    {
        case eAT25DF_BLOCK_4:
          instr = eAT25DF_BLOCK_4_ERASE;
        break;

        case eAT25DF_BLOCK_32:
          instr = eAT25DF_BLOCK_32_ERASE;
        break;

        case eAT25DF_BLOCk_64:
          instr = eAT25DF_BLOCK_64_ERASE;
        break;

        default:
          return eMEM_INTERNAL_ERROR;
    };

    SelectChip();

    // Write cmd
    SPI_BUFF = instr;

    // Write address
    for (uint32_t i = 0, k = 2; i < 3;) {
        if (SPI_FREE) {
            SPI_BUFF = _addr.addrArray[k--];
            i++;
        };
    };
    while (!(SPI_FREE));

    LockChip();

    return eMEM_OK;
}
//==============================================================================
//==============================================================================


//==============================================================================
// App: Chip erase
//==============================================================================
uint32_t AT25DF::ChipErase(const uint32_t _password) {
    // Test flash memory is Ok
    if (m_isFlashOk != TRUE_T) {
        return eMEM_NOT_AVAILABLE;
    };

    // Test psw
    if (_password != AT25DF_CHIP_ERASE_PSW) {
        return eMEM_INTERNAL_ERROR;
    };

    // Test busy
    if (IsBusy() == TRUE_T) {
        return eMEM_BUSY;
    };

    // Need global unprotect

    // Test write enable bit
    if (IsWriteEnable() == FALSE_T) {
        WriteEnable();

        if (IsWriteEnable() == FALSE_T) {
            return eMEM_WRITE_EN_ERROR;
        };
    };

    SelectChip();

    // Write cmd
    SPI_BUFF = eAT25DF_CHIP_ERASE;
    while (!(SPI_FREE));

    LockChip();

    return eMEM_OK;
}
//==============================================================================
//==============================================================================